#include "screencapture.h"
#include <QBuffer>
#include <QTcpSocket>
#include <QTime>
#include <windows.h>
#include <QApplication>


// constuctor
ScreenCapture::ScreenCapture(WId wid, int port){
    this->wid = wid;
    firstRun = true;
    sum = 0;
    number = 0;
    // find out screen resolution and save it
    QPixmap temp;
    temp = QPixmap::grabWindow(wid);
    resolutionX = temp.width();
    resolutionY = temp.height();
    quality = 0;

    // server listens on all network interfaces on port 3500
    if (!tcpserver.listen(QHostAddress::Any, port)) {
        qDebug()<<"Unable to activate server to listen on port "<<port<<"\n";
    }
    else
        qDebug()<<"Server listening for incoming conections on port "<<port<<"\n";
    // set event triggered in case of a client logon

    connect(&tcpserver, SIGNAL(newConnection()), this, SLOT(serveClient()));

}

//destructor
ScreenCapture::~ScreenCapture(){
    quality=0;
}

void ScreenCapture::run(){

}

void ScreenCapture::serveClient(){

    if(tcpserver.hasPendingConnections()){
        clientConnection = tcpserver.nextPendingConnection();
        connect(clientConnection, SIGNAL(readyRead()), this, SLOT(readFromClient()));
    }

    if(firstRun){
        time2.start();
    }
    sum += time2.elapsed();
    number++;
    qDebug() << "Avg time between update: " << sum/number;     // avg = 247
    time2.restart();
    QTime time;
    time.start();


    //qDebug("Client logon\n");

    // delete client connection after logof
    //connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater()));
    // when we receive packet / command from client



    pixmap = QPixmap::grabWindow(wid);

    // check for differences with previus picture and send only a square of differences
    //QPixmap pixmap2;

    QImage newImage = pixmap.toImage();
    //QImage newImage = newImage1.convertToFormat(QImage::Format_Indexed8,Qt::AutoColor);

    int minX = 0, minY = 0, maxX = 1, maxY = 1;
    if(!firstRun){
        bool minMaxSet = false;
        for(int y=0; y<image.height(); y++){
            for(int x=0; x<image.width(); x++){
                if(image.pixel(x,y) != newImage.pixel(x,y)){
                    if(!minMaxSet){
                        minX = x;
                        maxX = x;
                        minY = y;
                        maxY = y;
                        minMaxSet = true;
                    }
                    else{
                        if(x < minX)
                            minX = x;
                        if(x > maxX)
                            maxX = x;
                        if(y < minY)
                            minY = y;
                        if(y > maxY)
                            maxY = y;
                    }
                }
            }
        }
        image = newImage;
        int width = maxX - minX + 1;
        if(width > resolutionX)
            width = resolutionX;
        int height = maxY - minY + 1;
        if(height > resolutionY)
            height = resolutionY;
        newImage = newImage.copy(minX, minY, width, height);
        //pixmap2 = QPixmap::fromImage(newImage);
    }
    else {
        image = newImage;
        //pixmap2 = pixmap;
        firstRun = false;
    }

    // constuct data to send to client
    QByteArray ba;
    QBuffer buffer(&ba);
    buffer.open(QIODevice::WriteOnly);

    switch(quality){
        case 0: newImage.save(&buffer, "PNG");break;
        case 1: newImage.save(&buffer, "JPG");break;
    }

    QTime time2;
    time2.start();
    //ba = qCompress(ba,2); //zipping
    //qDebug() << "Compressing: "<< time2.elapsed();
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out << (quint32)ba.size();
    out << (quint16)minX; // x location where to paste on previus image
    out << (quint16)minY; // y location where to paste on previus image
    out << ba;
    //clientConnection->write(block);
    //qDebug() << "Time elapsed: " << time.elapsed() << " ms";
    qDebug() << "Bytes written: " << clientConnection->write(block);
}

// click (and hold) mouse button
void ScreenCapture::doMousePress(int key, int x, int y){ // key 0 == left mouse button, key 1 == right
    // move mouse position
    mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, x*((float)65535/resolutionX), y*((float)65535/resolutionY), 0, 0);
    // do a mouse press (and do not release it)
    if(key == 0) // left click
        mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
    else if(key == 1) // right click
        mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
}

// move mouse (while pressed)
void ScreenCapture::doMouseMove(int x, int y){
    // move mouse position
    mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, x*((float)65535/resolutionX), y*((float)65535/resolutionY), 0, 0);
}

// release mouse button
void ScreenCapture::doMouseRelease(int key, int x, int y){ // key 0 == left mouse button, key 1 == right
    // move mouse position
    mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, x*((float)65535/resolutionX), y*((float)65535/resolutionY), 0, 0);
    // do a mouse press (and do not release it)
    if(key == 0) // left click
        mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
    else if(key == 1) // right click
        mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
}

// press a keyboard key
void ScreenCapture::doKeyPress(int key){
    BYTE virtualKeyCode, scanKeyCode;
    scanKeyCode = (BYTE)key;
    // get windows virtual code of the key
    virtualKeyCode = (BYTE)MapVirtualKey(scanKeyCode, 1);
    // find out if the key is 'extended'
    bool extendedKey = false;
    if(key >= 57344)
        extendedKey = true;
    // trigger keyboard key press event
    if(extendedKey)
        keybd_event(virtualKeyCode, scanKeyCode, KEYEVENTF_EXTENDEDKEY | 0, 0);
    else
        keybd_event(virtualKeyCode, scanKeyCode, 0, 0);
}

void ScreenCapture::doKeyRelease(int key){
    BYTE virtualKeyCode, scanKeyCode;
    scanKeyCode = (BYTE)key;
    // get windows virtual code of the key
    virtualKeyCode = (BYTE)MapVirtualKey(scanKeyCode, 1);
    // find out if the key is 'extended'
    bool extendedKey = false;
    if(key >= 57344)
        extendedKey = true;
    // trigger keyboard key press event
    if(extendedKey)
        keybd_event(virtualKeyCode, scanKeyCode, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
    else
        keybd_event(virtualKeyCode, scanKeyCode, KEYEVENTF_KEYUP, 0);
}

// when client send a packet to server (requests for clicks etc.)
void ScreenCapture::readFromClient(){    
    QDataStream in(clientConnection);
    quint8 type;
    in >> type; // read type of packet

    // if type is 0 (request for left mouse press)
    if(type == 0){
        quint16 x, y;
        in >> x; // read X mouse press position from packet
        in >> y; // read Y mouse press position from packet
        qDebug() << "Left mouse press: X = " << x << ", Y = " << y;
        doMousePress(0, x, y);
    }
    else if(type == 1){ // request for right mouse press
        quint16 x, y;
        in >> x; // read X mouse press position from packet
        in >> y; // read Y mouse press position from packet
        qDebug() << "Right mouse press: X = " << x << ", Y = " << y;
        doMousePress(1, x, y);
    }
    else if(type == 2){ // request for left mouse release
        quint16 x, y;
        in >> x; // read X mouse release position from packet
        in >> y; // read Y mouse release position from packet
        qDebug() << "Left mouse release: X = " << x << ", Y = " << y;
        doMouseRelease(0, x, y);
    }
    else if(type == 3){ // request for right mouse release
        quint16 x, y;
        in >> x; // read X mouse release position from packet
        in >> y; // read Y mouse release position from packet
        qDebug() << "Right mouse release: X = " << x << ", Y = " << y;
        doMouseRelease(1, x, y);
    }
    else if(type == 4){ // request for mouse move (while left mouse is down)
        quint16 x, y;
        in >> x; // read X mouse release position from packet
        in >> y; // read Y mouse release position from packet
        qDebug() << "Mouse move: X = " << x << ", Y = " << y;
        doMouseMove(x, y);
    }
    else if(type == 5){ // request for keybord key press
        quint16 keyScanCode;
        in >> keyScanCode; // scan code of key
        qDebug() << "Key press: " << keyScanCode;
        doKeyPress(keyScanCode);
    }
    else if(type == 6){ // request for keybord key release
        quint16 keyScanCode;
        in >> keyScanCode; // scan code of key
        qDebug() << "Key release: " << keyScanCode;
        doKeyRelease(keyScanCode);
    }
    else if(type == 7){ //notification that packet is recived
        qDebug()<<"Client recived packet!";
        serveClient();
    }
    else if(type == 8){ //quality select
        in >> quality;
        qDebug()<<"quality selected: "<<quality;
        
    }
    if(clientConnection->bytesAvailable())
        readFromClient();
}
